package cn.sciento.iam.app.service.impl;

import java.util.LinkedList;
import java.util.List;

import cn.sciento.boot.platform.data.permission.util.DocRedisUtils;
import cn.sciento.core.base.BaseConstants;
import cn.sciento.iam.app.service.DocTypeDimensionService;
import cn.sciento.iam.app.service.DocTypeService;
import cn.sciento.iam.domain.entity.DocTypeDimension;
import cn.sciento.iam.domain.entity.RoleAuthority;
import cn.sciento.iam.domain.entity.RoleAuthorityLine;
import cn.sciento.iam.domain.entity.UserAuthority;
import cn.sciento.iam.domain.repository.DocTypeDimensionRepository;
import cn.sciento.iam.domain.repository.RoleAuthorityLineRepository;
import cn.sciento.iam.domain.repository.RoleAuthorityRepository;
import cn.sciento.iam.domain.repository.UserAuthorityRepository;
import cn.sciento.iam.infra.feign.PermissionRuleFeignClient;
import cn.sciento.mybatis.helper.UniqueHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

/**
 * 单据类型维度应用服务默认实现
 *


 */
@Service
public class DocTypeDimensionServiceImpl implements DocTypeDimensionService {
    @Autowired
    private DocTypeDimensionRepository dimensionRepository;
    @Autowired
    private UserAuthorityRepository userAuthorityRepository;
    @Autowired
    private RoleAuthorityRepository roleAuthorityRepository;
    @Autowired
    private RoleAuthorityLineRepository authorityLineRepository;
    @Autowired
    private DocTypeService docTypeService;
    @Autowired
    private PermissionRuleFeignClient feignClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DocTypeDimension createDocTypeDimension(DocTypeDimension docTypeDimension) {
        Assert.isTrue(UniqueHelper.valid(docTypeDimension), BaseConstants.ErrorCode.ERROR_CODE_REPEAT);
        dimensionRepository.insertSelective(docTypeDimension);
        return docTypeDimension;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DocTypeDimension updateDocTypeDimension(DocTypeDimension docTypeDimension) {
        DocTypeDimension dbDimension = dimensionRepository.selectByPrimaryKey(docTypeDimension.getDimensionId());
        dimensionRepository.updateOptional(docTypeDimension, DocTypeDimension.FIELD_DIMENSION_NAME,
                DocTypeDimension.FIELD_DIMENSION_TYPE, DocTypeDimension.FIELD_VALUE_SOURCE_TYPE,
                DocTypeDimension.FIELD_VALUE_SOURCE, DocTypeDimension.FIELD_ORDER_SEQ,
                DocTypeDimension.FIELD_ENABLED_FLAG);
        // 禁用单据维度需判断是否存在启用的单据权限与其关联，若存在则需清除相关数据（数据权限、单据权限缓存）
        if (docTypeDimension.getEnabledFlag().equals(BaseConstants.Flag.NO)
                && dbDimension.getEnabledFlag().equals(BaseConstants.Flag.YES)) {
            this.processDocCacheAndPermissionRule(docTypeDimension, false);
        } else if (docTypeDimension.getEnabledFlag().equals(BaseConstants.Flag.YES)
                && dbDimension.getEnabledFlag().equals(BaseConstants.Flag.NO)) {
            this.processDocCacheAndPermissionRule(docTypeDimension, true);
        }
        return docTypeDimension;
    }

    /**
     * 处理单据权限缓存以及数据权限内容
     *
     * @param docTypeDimension 单据维度
     * @param isEnabled 是否为从禁用到启用，true则添加缓存，false则删除缓存
     */
    private void processDocCacheAndPermissionRule(DocTypeDimension docTypeDimension, Boolean isEnabled) {
        // 判断维度是否关联启用状态的单据，若未关联则无需处理缓存及数据权限
        List<Long> docTypeIds = dimensionRepository.selectDocTypeIdByDimensionCode(docTypeDimension);
        if (CollectionUtils.isEmpty(docTypeIds)) {
            return;
        }
        // 处理用户缓存
        UserAuthority userAuthority = new UserAuthority();
        userAuthority.setAuthorityTypeCode(docTypeDimension.getDimensionCode());
        List<UserAuthority> userAuthorities = userAuthorityRepository.select(userAuthority);
        List<RoleAuthorityLine> roleAuthorityLines =
                authorityLineRepository.selectRoleAuthLineByAuthTypeCode(docTypeDimension.getDimensionCode());
        List<RoleAuthority> roleAuthorities = new LinkedList<>();
        docTypeIds.forEach(docTypeId -> {
            List<RoleAuthority> roleAuthorityList = roleAuthorityRepository.select(new RoleAuthority()
                    .setAuthDocTypeId(docTypeId)
                    .setAuthScopeCode(docTypeDimension.getDimensionType()));
            if (!CollectionUtils.isEmpty(roleAuthorityList)) {
                roleAuthorities.addAll(roleAuthorityList);
            }
        });
        if (isEnabled) {
            userAuthorities.forEach(ua ->
                    DocRedisUtils.setDocUserAuthRedis(ua.getTenantId(), ua.getAuthorityTypeCode(), ua.getUserId()));
            roleAuthorities.forEach(roleAuthority -> DocRedisUtils.setDocRoleAuthHeaderRedis(
                    roleAuthority.getAuthDocTypeId(),
                    roleAuthority.getAuthScopeCode(),
                    roleAuthority.getRoleId()
            ));
            roleAuthorityLines.forEach(roleAuthorityLine -> DocRedisUtils.setDocRoleAuthLineRedis(
                    roleAuthorityLine.getDocTypeId(),
                    roleAuthorityLine.getDimensionType(),
                    roleAuthorityLine.getAuthTypeCode(),
                    roleAuthorityLine.getRoleId()
            ));
        } else {
            userAuthorities.forEach(ua ->
                    DocRedisUtils.delDocUserAuthRedis(ua.getTenantId(), ua.getAuthorityTypeCode(), ua.getUserId()));
            roleAuthorities.forEach(roleAuthority -> DocRedisUtils.delDocRoleAuthHeaderRedis(
                    roleAuthority.getAuthDocTypeId(),
                    roleAuthority.getAuthScopeCode(),
                    roleAuthority.getRoleId()
            ));
            roleAuthorityLines.forEach(roleAuthorityLine -> DocRedisUtils.delDocRoleAuthLineRedis(
                    roleAuthorityLine.getDocTypeId(),
                    roleAuthorityLine.getDimensionType(),
                    roleAuthorityLine.getAuthTypeCode(),
                    roleAuthorityLine.getRoleId()
            ));
        }
        // 处理数据权限
        docTypeService.generateShieldRule(docTypeDimension.getTenantId(), docTypeIds);
    }
}
